import Konva from "konva";
import { Draw } from "../../../Draw/Draw";
import { IKonva } from "../../../Interface/IKonva";

/**
 * 对齐辅助线
 *  参考官网案例： http://konvajs-doc.bluehymn.com/docs/sandbox/Objects_Snapping.html
 *  辅助线 统记为  (#guid-line)
 */

// 开启磁吸线
const OFFSET = 5;

// 对齐辅助线基本配置
const BASELINE = {
  stroke: "rgba(255, 94, 0, 0.5)",
  strokeWidth: 2,
  dash: [5, 5],
  id: "guid-line",
};

type Position = {
  [key: string]: number;
  vl: number;
  vc: number;
  vr: number;
  ht: number;
  hc: number;
  hb: number;
};

/** 辅助函数 - 获取当前激活的节点坐标 */
function getCurrentNodePosition(e: IKonva.Event): Position {
  // target 指向当前被拖拽的元素
  const node = e.target;
  const scaleX = node.scaleX() || 1;
  const scaleY = node.scaleY() || 1;
  const x = node.x();
  const y = node.y();
  const w = node.width() * scaleX;
  const h = node.height() * scaleY;
  /**
   * 构造对象 {ht,hc,hb,vl,vc,vr} horizontalLine 水平线 verticalLine 垂直线
   *
   *    vl  vc   vr
   *    |   |    |
   *  ------------- ht
   *    |   |    |
   *  ------------- hc
   *    |   |    |
   *  ------------- hb
   *    |   |    |
   */
  const vl = x;
  const vc = x + w / 2;
  const vr = x + w;
  const ht = y;
  const hc = y + h / 2;
  const hb = y + h;
  return { vl, vc, vr, ht, hc, hb };
}

/** 辅助函数 - 获取当前所有节点的位置坐标 */
function getAllNodePosition(e: IKonva.Event, draw: Draw) {
  const positionCache: Position[] = [];
  const stage = draw.getStage();
  const { width, height } = stage.size();

  const layer = draw.getLayer();
  if (!layer) return;

  //   通过 find 找到所有 group
  const nodes = draw.getKonvaGraph().getBaseGraph();
  nodes.forEach((node) => {
    if (node.id() === e.target.id()) return;
    const ce = <IKonva.Event>{ target: node };
    const pos = getCurrentNodePosition(ce);
    positionCache.push(pos);
  });
  // 还需要将边界也添加进去
  positionCache.push({
    vl: 0,
    vc: width / 2,
    vr: width,
    ht: 0,
    hc: height / 2,
    hb: height,
  });

  return positionCache;
}

/** 辅助函数 - 修正节点坐标，实现磁吸效果 */
function fixNodePosition(
  node: Konva.Node,
  poc: Position,
  pockey: string,
  nodeDirection: string
) {
  const width = node.width() * (node.scaleX() || 1);
  const height = node.height() * (node.scaleY() || 1);

  //   被检测元素位置 - 当前拖拽元素
  const switchKey = pockey + "-" + nodeDirection;
  switch (switchKey) {
    // 拖拽元素的 ht 顶部对齐
    case "ht-ht":
      return node.y(poc.ht);
    case "hc-ht":
      return node.y(poc.hc);
    case "hb-ht":
      return node.y(poc.hb);

    case "ht-hc":
      return node.y(poc.ht - height / 2);
    case "hc-hc":
      return node.y(poc.hc - height / 2);
    case "hb-hc":
      return node.y(poc.hb - height / 2);

    case "ht-hb":
      return node.y(poc.ht - height);
    case "hc-hb":
      return node.y(poc.hc - height);
    case "hb-hb":
      return node.y(poc.hb - height);

    case "vl-vl":
      return node.x(poc.vl);
    case "vc-vl":
      return node.x(poc.vc);
    case "vr-vl":
      return node.x(poc.vr);

    case "vl-vc":
      return node.x(poc.vl - width / 2);
    case "vc-vc":
      return node.x(poc.vc - width / 2);
    case "vr-vc":
      return node.x(poc.vr - width / 2);

    case "vl-vr":
      return node.x(poc.vl - width);
    case "vc-vr":
      return node.x(poc.vc - width);
    case "vr-vr":
      return node.x(poc.vr - width);
  }
}

/**
 * 清空对齐辅助线
 * @param draw Draw 对象
 * @returns
 */
export function clearGuidLine(draw: Draw) {
  const layer = draw.getLayer();
  if (!layer) return;
  //   找所有的 guid-line
  layer.find("#guid-line").forEach((line) => line.destroy());
  layer.draw();
}

/**
 * 添加对齐辅助线
 * @param draw
 * @returns
 */
export function addGuidLine(e: IKonva.Event, draw: Draw) {
  clearGuidLine(draw);
  const layer = draw.getLayer();
  if (!layer) return;

  const { width, height } = layer.size();
  const node = e.target;

  // 获取当前拖拽节点的坐标
  const pos = getCurrentNodePosition(e);

  //   获取所有节点位置信息 - 避免重复计算，非拖拽元素的位置信息不变
  const positionCache = getAllNodePosition(e, draw);

  // 进行坐标比较，添加 guid-line
  positionCache?.forEach((poc) => {
    for (const key in poc) {
      if (Object.prototype.hasOwnProperty.call(poc, key)) {
        const value = poc[key];

        // 水平比水平 水平就看 ht hc hb
        if (key.includes("h")) {
          if (Math.abs(value - pos.ht) < OFFSET) {
            // 其他graph的key位置与当前拖拽元素的 ht 位置重合
            const points = [0, value, width, value];
            const line = new Konva.Line({ ...BASELINE, points });
            fixNodePosition(node, poc, key, "ht");
            layer.add(line);
          }
          if (Math.abs(value - pos.hc) < OFFSET) {
            // 其他graph的key位置与当前拖拽元素的 hc 位置重合
            const points = [0, value, width, value];
            const line = new Konva.Line({ ...BASELINE, points });
            fixNodePosition(node, poc, key, "hc");
            layer.add(line);
          }
          if (Math.abs(value - pos.hb) < OFFSET) {
            // 其他graph的key位置与当前拖拽元素的 hb 位置重合
            const points = [0, value, width, value];
            const line = new Konva.Line({ ...BASELINE, points });
            fixNodePosition(node, poc, key, "hb");
            layer.add(line);
          }
        }
        // 垂直比垂直 垂直就看 vl vc vr
        if (key.includes("v")) {
          if (Math.abs(value - pos.vl) < OFFSET) {
            // 其他graph的key位置与当前拖拽元素的 vl 位置重合
            const points = [value, 0, value, height];
            const line = new Konva.Line({ ...BASELINE, points });
            fixNodePosition(node, poc, key, "vl");
            layer.add(line);
          }
          if (Math.abs(value - pos.vc) < OFFSET) {
            // 其他graph的key位置与当前拖拽元素的 vc 位置重合
            const points = [value, 0, value, height];
            const line = new Konva.Line({ ...BASELINE, points });
            fixNodePosition(node, poc, key, "vc");
            layer.add(line);
          }
          if (Math.abs(value - pos.vr) < OFFSET) {
            // 其他graph的key位置与当前拖拽元素的 vr 位置重合
            const points = [value, 0, value, height];
            const line = new Konva.Line({ ...BASELINE, points });
            fixNodePosition(node, poc, key, "vr");
            layer.add(line);
          }
        }
      }
    }
  });

  layer.draw();
}
